package org.libraq.hrm.service.impl;

import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.libraq.hrm.doc.CourseDoc;
import org.libraq.hrm.dto.CourseSearchDto;
import org.libraq.hrm.util.PageList;
import org.libraq.hrm.vo.AggCourseVo;
import org.libraq.hrm.mapper.HighlightResultMapper;
import org.libraq.hrm.repository.CourseElasticsearchRepository;
import org.libraq.hrm.service.ICourseEsSearch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class CourseEsSearchImpl implements ICourseEsSearch {
    @Autowired
    private CourseElasticsearchRepository courseElasticsearchRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;

    @Override
    public PageList<CourseDoc> searchCourse(CourseSearchDto dto) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //1.封装查询参数  dto -> NativeSearchQuery
        //====================查询条件======
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if(StringUtils.hasLength(dto.getKeyword())){
            //boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
            boolQuery.must(QueryBuilders.multiMatchQuery(dto.getKeyword(),"name","tenantName"));
        }
        if(dto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",dto.getCourseTypeId()));
        }
        if(StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",dto.getGradeName()));
        }
        if(dto.getPriceMin() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        if(dto.getPriceMax() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }
        if(StringUtils.hasLength(dto.getTenantName())){
            //boolQuery.filter(QueryBuilders.termQuery("tenantName",dto.getTenantName()));
        }
        //设置查询query
        //设置高亮显示
        HighlightBuilder.Field field = new HighlightBuilder.Field("name").preTags("<font style='color:red'><b>").postTags("</b></font>");
        builder.withHighlightFields(field);  // 名字高亮
        builder.withQuery(boolQuery);
        //========聚合查询
        // 机构名字聚合
        TermsAggregationBuilder tenantNameAgg = AggregationBuilders.terms("tenantNameAgg")//聚合之后的别名
                .field("tenantName") //根据哪个字段聚合
                .order(Terms.Order.count(false))//按照聚合结果count降序
                .size(10);// 取前10个

        // 等级名字聚合
        TermsAggregationBuilder gradeNameAgg = AggregationBuilders.terms("gradeNameAgg")//聚合之后的别名
                .field("gradeName") //根据哪个字段聚合
                .order(Terms.Order.count(false))//按照聚合结果count降序
                .size(10);// 取前10个
        builder.addAggregation(tenantNameAgg).addAggregation(gradeNameAgg);//添加聚合条件

        //======================排序字段
        if(StringUtils.hasLength(dto.getSortField())){
            String filedName = "startTime";
            switch (dto.getSortField().toLowerCase()){
                case "xl": filedName = "saleCount";break;
                case "xp": filedName = "startTime";break;
                case "pl": filedName = "commentCount";break;
                case "jg": filedName = "price";break;
                case "rq": filedName = "viewCount";break;
                default:filedName = "startTime";break;
            }
            SortOrder sortOrder = SortOrder.DESC;
            if(dto.getSortType().equals("asc")){
                sortOrder = SortOrder.ASC;
            }
            builder.withSort(new FieldSortBuilder(filedName).order(sortOrder));
        }
        builder.withPageable(PageRequest.of(dto.getPage() -1, dto.getRows()));
        //2.执行查询
        /* Page<CourseDoc> page = courseElasticsearchRepository.search(builder.build());*/
        AggregatedPage<CourseDoc> page = elasticsearchTemplate.queryForPage(
                builder.build(),
                CourseDoc.class,
                highlightResultMapper);

        Aggregations aggregations = page.getAggregations();//得到集合结果
        Map<String, Aggregation> asMap = aggregations.asMap();//将聚合结果以map的方式返回给我
        Set<Map.Entry<String, Aggregation>> entries = asMap.entrySet();
        Map<String,List<AggCourseVo>> aggMap =  new HashMap<>();
        entries.forEach(enty->{
            String key = enty.getKey();//我们定义的别名  tenantNameAgg
            // enty.getValue() 是上层对象，我们需要的是对象中的Buckets
            List<StringTerms.Bucket> buckets = ((StringTerms) enty.getValue()).getBuckets();//Buckets 聚合结果
            List<AggCourseVo> aggCourseVos = buckets.stream().map(bucket -> {
                //把List中的Bucket  转换成  AggCourseVo
                return new AggCourseVo(bucket.getKeyAsString(), bucket.getDocCount());
            }).collect(Collectors.toList());
            aggMap.put(key,aggCourseVos);
        });

        //3.将 Page  转换成PageList并返回
        return new PageList<>(
                page.getTotalElements(),//总条数
                page.getContent(),  //当前页的数据
                aggMap      //聚合结果
        );
    }
}
